import { computed, getCurrentInstance, nextTick, onMounted, ref, watch } from 'vue';
import { isClient, useTimeoutFn } from '@vueuse/core';
import { addUnit } from './utils';

import type { Ref, SetupContext } from 'vue';
import type { DialogEmits, DialogProps } from './dialog';

export const useDialog = (props: DialogProps, targetRef: Ref<HTMLElement | undefined>) => {
  const instance = getCurrentInstance()!;
  const emit = instance.emit as SetupContext<DialogEmits>['emit'];

  let lastPosition = '';
  const visible = ref(false);
  const closed = ref(false);
  const rendered = ref(false); // when desctroyOnClose is true, we initialize it as false vise versa
  const zIndex = ref(props.zIndex || 999);

  let openTimer: (() => void) | undefined = undefined;
  let closeTimer: (() => void) | undefined = undefined;

  const namespace = ref('el');

  const style = computed(() => {
    const style: any = {};
    const varPrefix = `--${namespace.value}-dialog` as const;
    if (!props.fullscreen) {
      if (props.top) {
        style[`${varPrefix}-margin-top`] = props.top;
      }
      if (props.width) {
        style[`${varPrefix}-width`] = addUnit(props.width);
      }
    }
    return style;
  });

  const overlayDialogStyle = computed(() => {
    if (props.alignCenter) {
      return { display: 'flex' };
    }
    return {};
  });

  function afterEnter() {
    emit('opened');
  }

  function afterLeave() {
    emit('closed');
    emit('update:modelValue', false);
    if (props.destroyOnClose) {
      rendered.value = false;
    }
  }

  function beforeLeave() {
    emit('close');
  }

  function open() {
    closeTimer?.();
    openTimer?.();

    if (props.openDelay && props.openDelay > 0) {
      ({ stop: openTimer } = useTimeoutFn(() => doOpen(), props.openDelay));
    } else {
      doOpen();
    }
  }

  function close() {
    openTimer?.();
    closeTimer?.();

    if (props.closeDelay && props.closeDelay > 0) {
      ({ stop: closeTimer } = useTimeoutFn(() => doClose(), props.closeDelay));
    } else {
      doClose();
    }
  }

  function handleClose() {
    function hide(shouldCancel?: boolean) {
      if (shouldCancel) return;
      closed.value = true;
      visible.value = false;
    }

    if (props.beforeClose) {
      props.beforeClose(hide);
    } else {
      close();
    }
  }

  function onModalClick() {
    if (props.closeOnClickModal) {
      handleClose();
    }
  }

  function doOpen() {
    if (!isClient) return;
    visible.value = true;
  }

  function doClose() {
    visible.value = false;
  }

  function onOpenAutoFocus() {
    emit('openAutoFocus');
  }

  function onCloseAutoFocus() {
    emit('closeAutoFocus');
  }

  function onFocusoutPrevented(event: CustomEvent) {
    if (event.detail?.focusReason === 'pointer') {
      event.preventDefault();
    }
  }

  function onCloseRequested() {
    if (props.closeOnPressEscape) {
      handleClose();
    }
  }

  watch(
    () => props.modelValue,
    val => {
      if (val) {
        closed.value = false;
        open();
        rendered.value = true; // enables lazy rendering
        zIndex.value = props.zIndex ? zIndex.value++ : 999;
        // this.$el.addEventListener('scroll', this.updatePopper)
        nextTick(() => {
          emit('open');
          if (targetRef.value) {
            targetRef.value.scrollTop = 0;
          }
        });
      } else {
        // this.$el.removeEventListener('scroll', this.updatePopper
        if (visible.value) {
          close();
        }
      }
    }
  );

  watch(
    () => props.fullscreen,
    val => {
      if (!targetRef.value) return;
      if (val) {
        lastPosition = targetRef.value.style.transform;
        targetRef.value.style.transform = '';
      } else {
        targetRef.value.style.transform = lastPosition;
      }
    }
  );

  onMounted(() => {
    if (props.modelValue) {
      visible.value = true;
      rendered.value = true; // enables lazy rendering
      open();
    }
  });

  return {
    afterEnter,
    afterLeave,
    beforeLeave,
    handleClose,
    onModalClick,
    close,
    doClose,
    onOpenAutoFocus,
    onCloseAutoFocus,
    onCloseRequested,
    onFocusoutPrevented,
    closed,
    style,
    overlayDialogStyle,
    rendered,
    visible,
    zIndex
  };
};
